home *** CD-ROM | disk | FTP | other *** search
- /*
- Copyright 1991,1992 Eric R. Smith.
- Copyright 1992,1993,1994 Atari Corporation.
- All rights reserved.
- */
-
- /* a simple unified file system */
-
- #include "mint.h"
-
-
- extern FILESYS bios_filesys, proc_filesys, pipe_filesys, shm_filesys;
-
- static long ARGS_ON_STACK uni_root P_((int drv, fcookie *fc));
- static long ARGS_ON_STACK uni_lookup P_((fcookie *dir, const char *name, fcookie *fc));
- static long ARGS_ON_STACK uni_getxattr P_((fcookie *fc, XATTR *xattr));
- static long ARGS_ON_STACK uni_chattr P_((fcookie *fc, int attrib));
- static long ARGS_ON_STACK uni_chown P_((fcookie *fc, int uid, int gid));
- static long ARGS_ON_STACK uni_chmode P_((fcookie *fc, unsigned mode));
- static long ARGS_ON_STACK uni_rmdir P_((fcookie *dir, const char *name));
- static long ARGS_ON_STACK uni_remove P_((fcookie *dir, const char *name));
- static long ARGS_ON_STACK uni_getname P_((fcookie *root, fcookie *dir,
- char *pathname, int size));
- static long ARGS_ON_STACK uni_rename P_((fcookie *olddir, char *oldname,
- fcookie *newdir, const char *newname));
- static long ARGS_ON_STACK uni_opendir P_((DIR *dirh, int flags));
- static long ARGS_ON_STACK uni_readdir P_((DIR *dirh, char *nm, int nmlen, fcookie *));
- static long ARGS_ON_STACK uni_rewinddir P_((DIR *dirh));
- static long ARGS_ON_STACK uni_closedir P_((DIR *dirh));
- static long ARGS_ON_STACK uni_pathconf P_((fcookie *dir, int which));
- static long ARGS_ON_STACK uni_dfree P_((fcookie *dir, long *buf));
- static DEVDRV * ARGS_ON_STACK uni_getdev P_((fcookie *fc, long *devsp));
- static long ARGS_ON_STACK uni_symlink P_((fcookie *dir, const char *name, const char *to));
- static long ARGS_ON_STACK uni_readlink P_((fcookie *fc, char *buf, int buflen));
- static long ARGS_ON_STACK uni_fscntl P_((fcookie *dir, const char *name, int cmd, long arg));
-
- FILESYS uni_filesys = {
- (FILESYS *)0,
- FS_LONGPATH,
- uni_root,
- uni_lookup, nocreat, uni_getdev, uni_getxattr,
- uni_chattr, uni_chown, uni_chmode,
- nomkdir, uni_rmdir, uni_remove, uni_getname, uni_rename,
- uni_opendir, uni_readdir, uni_rewinddir, uni_closedir,
- uni_pathconf, uni_dfree, nowritelabel, noreadlabel,
- uni_symlink, uni_readlink, nohardlink, uni_fscntl, nodskchng
- };
-
- /*
- * structure that holds files
- * if (mode & S_IFMT == S_IFDIR), then this is an alias for a drive:
- * "dev" holds the appropriate BIOS device number, and
- * "data" is meaningless
- * if (mode & S_IFMT == S_IFLNK), then this is a symbolic link:
- * "dev" holds the user id of the owner, and
- * "data" points to the actual link data
- */
-
- typedef struct unifile {
- char name[NAME_MAX+1];
- ushort mode;
- ushort dev;
- FILESYS *fs;
- void *data;
- struct unifile *next;
- } UNIFILE;
-
- static UNIFILE u_drvs[UNI_NUM_DRVS];
- static UNIFILE *u_root = 0;
-
- static long do_ulookup P_((fcookie *, const char *, fcookie *, UNIFILE **));
-
- FILESYS *
- get_filesys (dev)
- int dev;
- {
- UNIFILE *u;
-
- for (u = u_root; u; u = u->next)
- if (u->dev == dev)
- return u->fs;
- return (FILESYS *) 0L;
- }
-
- void
- unifs_init()
- {
- UNIFILE *u = u_drvs;
- int i;
-
- u_root = u;
- for (i = 0; i < UNI_NUM_DRVS; i++,u++) {
- u->next = u+1;
- u->mode = S_IFDIR|DEFAULT_DIRMODE;
- u->dev = i;
- if (i == PROCDRV) {
- strcpy(u->name, "proc");
- u->fs = &proc_filesys;
- } else if (i == PIPEDRV) {
- strcpy(u->name, "pipe");
- u->fs = &pipe_filesys;
- } else if (i == BIOSDRV) {
- strcpy(u->name, "dev");
- u->fs = &bios_filesys;
- } else if (i == UNIDRV) {
- (u-1)->next = u->next; /* skip this drive */
- } else if (i == SHMDRV) {
- strcpy(u->name, "shm");
- u->fs = &shm_filesys;
- } else {
- u->name[0] = i + 'a';
- u->name[1] = 0;
- u->fs = 0;
- }
- }
- --u; /* oops, we went too far */
- u->next = 0;
- }
-
- static long ARGS_ON_STACK
- uni_root(drv, fc)
- int drv;
- fcookie *fc;
- {
- if (drv == UNIDRV) {
- fc->fs = &uni_filesys;
- fc->dev = drv;
- fc->index = 0L;
- return 0;
- }
- fc->fs = 0;
- return EINTRN;
- }
-
- static long ARGS_ON_STACK
- uni_lookup(dir, name, fc)
- fcookie *dir;
- const char *name;
- fcookie *fc;
- {
- return do_ulookup(dir, name, fc, (UNIFILE **)0);
- }
-
- /* worker function for uni_lookup; can also return the UNIFILE
- * pointer for the root directory
- */
- static long
- do_ulookup(dir, name, fc, up)
- fcookie *dir;
- const char *name;
- fcookie *fc;
- UNIFILE **up;
- {
- UNIFILE *u;
- long drvs;
- FILESYS *fs;
- fcookie *tmp;
- extern long dosdrvs;
-
- TRACE(("uni_lookup(%s)", name));
-
- if (dir->index != 0) {
- DEBUG(("uni_lookup: bad directory"));
- return EPTHNF;
- }
- /* special case: an empty name in a directory means that directory */
- /* so do "." and ".." */
-
- if (!*name || !strcmp(name, ".") || !strcmp(name, "..")) {
- dup_cookie(fc, dir);
- return 0;
- }
- drvs = drvmap() | dosdrvs | PSEUDODRVS;
- /*
- * OK, check the list of aliases and special directories
- */
- for (u = u_root; u; u = u->next) {
- if (!stricmp(name, u->name)) {
- if ( (u->mode & S_IFMT) == S_IFDIR ) {
- if (u->dev >= NUM_DRIVES) {
- fs = u->fs;
- if (up) *up = u;
- return (*fs->root)(u->dev,fc);
- }
- if ((drvs & (1L << u->dev)) == 0)
- return EPTHNF;
- tmp = &curproc->root[u->dev];
- if (!tmp->fs) { /* drive changed? */
- changedrv(tmp->dev);
- tmp = &curproc->root[u->dev];
- if (!tmp->fs)
- return EPTHNF;
- }
- dup_cookie(fc, tmp);
- } else { /* a symbolic link */
- fc->fs = &uni_filesys;
- fc->dev = UNIDRV;
- fc->index = (long)u;
- }
- if (up) *up = u;
- return 0;
- }
- }
- DEBUG(("uni_lookup: name (%s) not found", name));
- return EFILNF;
- }
-
- static long ARGS_ON_STACK
- uni_getxattr(fc, xattr)
- fcookie *fc;
- XATTR *xattr;
- {
- UNIFILE *u = (UNIFILE *)fc->index;
-
- if (fc->fs != &uni_filesys) {
- ALERT("ERROR: wrong file system getxattr called");
- return EINTRN;
- }
-
- xattr->index = fc->index;
- xattr->dev = fc->dev;
- xattr->nlink = 1;
- xattr->blksize = 1;
-
- /* If "u" is null, then we have the root directory, otherwise
- * we use the UNIFILE structure to get the info about it
- */
- if (!u || ( (u->mode & S_IFMT) == S_IFDIR )) {
- xattr->uid = xattr->gid = 0;
- xattr->size = xattr->nblocks = 0;
- xattr->mode = S_IFDIR | DEFAULT_DIRMODE;
- xattr->attr = FA_DIR;
- } else {
- xattr->uid = u->dev;
- xattr->gid = 0;
- xattr->size = xattr->nblocks = strlen(u->data) + 1;
- xattr->mode = u->mode;
- xattr->attr = 0;
- }
- xattr->mtime = xattr->atime = xattr->ctime = 0;
- xattr->mdate = xattr->adate = xattr->cdate = 0;
- return 0;
- }
-
- static long ARGS_ON_STACK
- uni_chattr(dir, attrib)
- fcookie *dir;
- int attrib;
- {
- UNUSED(dir); UNUSED(attrib);
- return EACCDN;
- }
-
- static long ARGS_ON_STACK
- uni_chown(dir, uid, gid)
- fcookie *dir;
- int uid, gid;
- {
- UNUSED(dir); UNUSED(uid);
- UNUSED(gid);
- return EINVFN;
- }
-
- static long ARGS_ON_STACK
- uni_chmode(dir, mode)
- fcookie *dir;
- unsigned mode;
- {
- UNUSED(dir);
- UNUSED(mode);
- return EINVFN;
- }
-
- static long ARGS_ON_STACK
- uni_rmdir(dir, name)
- fcookie *dir;
- const char *name;
- {
- long r;
-
- r = uni_remove(dir, name);
- if (r == EFILNF) r = EPTHNF;
- return r;
- }
-
- static long ARGS_ON_STACK
- uni_remove(dir, name)
- fcookie *dir;
- const char *name;
- {
- UNIFILE *u, *lastu;
-
- UNUSED(dir);
-
- lastu = 0;
- u = u_root;
- while (u) {
- if (!strncmp(u->name, name, NAME_MAX)) {
- if ( (u->mode & S_IFMT) != S_IFLNK ) return EFILNF;
- kfree(u->data);
- if (lastu)
- lastu->next = u->next;
- else
- u_root = u->next;
- kfree(u);
- return 0;
- }
- lastu = u;
- u = u->next;
- }
- return EFILNF;
- }
-
- static long ARGS_ON_STACK
- uni_getname(root, dir, pathname, size)
- fcookie *root, *dir; char *pathname;
- int size;
- {
- FILESYS *fs;
- UNIFILE *u;
- char *n;
- fcookie relto;
- char tmppath[PATH_MAX];
- long r;
-
- UNUSED(root);
-
- if (size <= 0) return ERANGE;
-
- fs = dir->fs;
- if (dir->dev == UNIDRV) {
- *pathname = 0;
- return 0;
- }
-
- for (u = u_root; u; u = u->next) {
- if (dir->dev == u->dev && (u->mode & S_IFMT) == S_IFDIR) {
- *pathname++ = '\\';
- if (--size <= 0) return ERANGE;
- for (n = u->name; *n; ) {
- *pathname++ = *n++;
- if (--size <= 0) return ERANGE;
- }
- break;
- }
- }
-
- if (!u) {
- ALERT("unifs: couldn't match a drive with a directory");
- return EPTHNF;
- }
-
- if (dir->dev >= NUM_DRIVES) {
- if ((*fs->root)(dir->dev, &relto) == 0) {
- if (!(fs->fsflags & FS_LONGPATH)) {
- r = (*fs->getname)(&relto, dir, tmppath, PATH_MAX);
- release_cookie(&relto);
- if (r) {
- return r;
- }
- if (strlen(tmppath) < size) {
- strcpy(pathname, tmppath);
- return 0;
- } else {
- return ERANGE;
- }
- }
- r = (*fs->getname)(&relto, dir, pathname, size);
- release_cookie(&relto);
- return r;
- } else {
- *pathname = 0;
- return EINTRN;
- }
- }
-
- if (curproc->root[dir->dev].fs != fs) {
- ALERT("unifs: drive's file system doesn't match directory's");
- return EINTRN;
- }
-
- if (!(fs->fsflags & FS_LONGPATH)) {
- r = (*fs->getname)(&curproc->root[dir->dev], dir, tmppath, PATH_MAX);
- if (r) return r;
- if (strlen(tmppath) < size) {
- strcpy(pathname, tmppath);
- return 0;
- } else {
- return ERANGE;
- }
- }
- return (*fs->getname)(&curproc->root[dir->dev], dir, pathname, size);
- }
-
- static long ARGS_ON_STACK
- uni_rename(olddir, oldname, newdir, newname)
- fcookie *olddir;
- char *oldname;
- fcookie *newdir;
- const char *newname;
- {
- UNIFILE *u;
- fcookie fc;
- long r;
-
- UNUSED(olddir);
-
- for (u = u_root; u; u = u->next) {
- if (!stricmp(u->name, oldname))
- break;
- }
-
- if (!u) {
- DEBUG(("uni_rename: old file not found"));
- return EFILNF;
- }
-
- /* the new name is not allowed to exist! */
- r = uni_lookup(newdir, newname, &fc);
- if (r == 0)
- release_cookie(&fc);
-
- if (r != EFILNF) {
- DEBUG(("uni_rename: error %ld", r));
- return (r == 0) ? EACCDN : r;
- }
-
- (void)strncpy(u->name, newname, NAME_MAX);
- return 0;
- }
-
- static long ARGS_ON_STACK
- uni_opendir(dirh, flags)
- DIR *dirh;
- int flags;
- {
- UNUSED(flags);
-
- if (dirh->fc.index != 0) {
- DEBUG(("uni_opendir: bad directory"));
- return EPTHNF;
- }
- dirh->index = 0;
- return 0;
- }
-
-
- static long ARGS_ON_STACK
- uni_readdir(dirh, name, namelen, fc)
- DIR *dirh;
- char *name;
- int namelen;
- fcookie *fc;
- {
- long map;
- char *dirname;
- int i;
- int giveindex = (dirh->flags == 0);
- UNIFILE *u;
- long index;
- extern long dosdrvs;
- long r;
-
- map = dosdrvs | drvmap() | PSEUDODRVS;
- i = dirh->index++;
- u = u_root;
- while (i > 0) {
- --i;
- u = u->next;
- if (!u)
- break;
- }
- tryagain:
- if (!u) return ENMFIL;
-
- dirname = u->name;
- index = (long)u;
- if ( (u->mode & S_IFMT) == S_IFDIR ) {
- /* make sure the drive really exists */
- if ( u->dev >= NUM_DRIVES) {
- r = (*u->fs->root)(u->dev,fc);
- if (r) {
- fc->fs = &uni_filesys;
- fc->index = 0;
- fc->dev = u->dev;
- }
- } else {
- if ((map & (1L << u->dev)) == 0 ) {
- dirh->index++;
- u = u->next;
- goto tryagain;
- }
- dup_cookie(fc, &curproc->root[u->dev]);
- if (!fc->fs) { /* drive not yet initialized */
- /* use default attributes */
- fc->fs = &uni_filesys;
- fc->index = 0;
- fc->dev = u->dev;
- }
- }
- } else { /* a symbolic link */
- fc->fs = &uni_filesys;
- fc->dev = UNIDRV;
- fc->index = (long)u;
- }
-
- if (giveindex) {
- namelen -= (int)sizeof(long);
- if (namelen <= 0) {
- release_cookie(fc);
- return ERANGE;
- }
- *((long *)name) = index;
- name += sizeof(long);
- }
- strncpy(name, dirname, namelen-1);
- if (strlen(name) < strlen(dirname)) {
- release_cookie(fc);
- return ENAMETOOLONG;
- }
- return 0;
- }
-
- static long ARGS_ON_STACK
- uni_rewinddir(dirh)
- DIR *dirh;
- {
- dirh->index = 0;
- return 0;
- }
-
- static long ARGS_ON_STACK
- uni_closedir(dirh)
- DIR *dirh;
- {
- UNUSED(dirh);
- return 0;
- }
-
- static long ARGS_ON_STACK
- uni_pathconf(dir, which)
- fcookie *dir;
- int which;
- {
- UNUSED(dir);
-
- switch(which) {
- case -1:
- return DP_MAXREQ;
- case DP_IOPEN:
- return 0; /* no files to open */
- case DP_MAXLINKS:
- return 1; /* no hard links available */
- case DP_PATHMAX:
- return PATH_MAX;
- case DP_NAMEMAX:
- return NAME_MAX;
- case DP_ATOMIC:
- return 1; /* no atomic writes */
- case DP_TRUNC:
- return DP_AUTOTRUNC;
- case DP_CASE:
- return DP_CASEINSENS;
- default:
- return EINVFN;
- }
- }
-
- static long ARGS_ON_STACK
- uni_dfree(dir, buf)
- fcookie *dir;
- long *buf;
- {
- UNUSED(dir);
-
- buf[0] = 0; /* number of free clusters */
- buf[1] = 0; /* total number of clusters */
- buf[2] = 1; /* sector size (bytes) */
- buf[3] = 1; /* cluster size (sectors) */
- return 0;
- }
-
- static DEVDRV * ARGS_ON_STACK
- uni_getdev(fc, devsp)
- fcookie *fc;
- long *devsp;
- {
- UNUSED(fc);
-
- *devsp = EACCDN;
- return 0;
- }
-
- static long ARGS_ON_STACK
- uni_symlink(dir, name, to)
- fcookie *dir;
- const char *name;
- const char *to;
- {
- UNIFILE *u;
- fcookie fc;
- long r;
-
- r = uni_lookup(dir, name, &fc);
- if (r == 0) {
- release_cookie(&fc);
- return EACCDN; /* file already exists */
- }
- if (r != EFILNF) return r; /* some other error */
-
- u = kmalloc(SIZEOF(UNIFILE));
- if (!u) return EACCDN;
-
- strncpy(u->name, name, NAME_MAX);
- u->name[NAME_MAX] = 0;
-
- u->data = kmalloc((long)strlen(to)+1);
- if (!u->data) {
- kfree(u);
- return EACCDN;
- }
- strcpy(u->data, to);
- u->mode = S_IFLNK | DEFAULT_DIRMODE;
- u->dev = curproc->ruid;
- u->next = u_root;
- u->fs = &uni_filesys;
- u_root = u;
- return 0;
- }
-
- static long ARGS_ON_STACK
- uni_readlink(fc, buf, buflen)
- fcookie *fc;
- char *buf;
- int buflen;
- {
- UNIFILE *u;
-
- u = (UNIFILE *)fc->index;
- assert(u);
- assert((u->mode & S_IFMT) == S_IFLNK);
- assert(u->data);
- strncpy(buf, u->data, buflen);
- if (strlen(u->data) >= buflen)
- return ENAMETOOLONG;
- return 0;
- }
-
-
-
-
- /* uk: use these Dcntl's to install a new filesystem which is only visible
- * on drive u:
- *
- * FS_INSTALL: let the kernel know about the file system; it does NOT
- * get a device number.
- * FS_MOUNT: use Dcntl(FS_MOUNT, "u:\\foo", &descr) to make a directory
- * foo where the filesytem resides in; the file system now
- * gets its device number which is also written into the
- * dev_no field of the fs_descr structure.
- * FS_UNMOUNT: remove a file system's directory; this call closes all
- * open files, directory searches and directories on this
- * device. Make sure that the FS will not recognise any
- * accesses to this device, as fs->root will be called
- * during the reinitalisation!
- * FS_UNINSTALL: remove a file system completely from the kernel list,
- * but that will only be possible if there is no directory
- * associated with this file system.
- * This function allows it to write file systems as demons
- * which stay in memory only as long as needed.
- *
- * BUG: it is not possible yet to lock such a filesystem.
- */
-
- /* here we start with gemdos only file system device numbers */
- static curr_dev_no = 0x100;
-
-
-
- static long ARGS_ON_STACK
- uni_fscntl(dir, name, cmd, arg)
- fcookie *dir;
- const char *name;
- int cmd;
- long arg;
- {
- fcookie fc;
- long r;
-
- extern struct kerinfo kernelinfo;
- extern FILESYS *active_fs;
-
- if (cmd == FS_INSTALL) { /* install a new filesystem */
- struct fs_descr *d = (struct fs_descr*)arg;
- FILESYS *fs;
-
- /* check if FS is installed already */
- for (fs = active_fs; fs; fs = fs->next)
- if (d->file_system == fs) return 0L;
- /* include new file system into chain of file systems */
- d->file_system->next = active_fs;
- active_fs = d->file_system;
- return (long)&kernelinfo; /* return pointer to kernel info as OK */
- } else if (cmd == FS_MOUNT) { /* install a new gemdos-only device for this FS */
- struct fs_descr *d = (struct fs_descr*)arg;
- FILESYS *fs;
- UNIFILE *u;
-
- /* first check for existing names */
- r = uni_lookup(dir, name, &fc);
- if (r == 0) {
- release_cookie(&fc);
- return EACCDN; /* name exists already */
- }
- if (r != EFILNF) return r; /* some other error */
- if (!d) return EACCDN;
- if (!d->file_system) return EACCDN;
- /* check if FS is installed */
- for (fs = active_fs; fs; fs = fs->next)
- if (d->file_system == fs) break;
- if (!fs) return EACCDN; /* not installed, so return an error */
- u = kmalloc(SIZEOF(UNIFILE));
- if (!u) return EACCDN;
- strncpy(u->name, name, NAME_MAX);
- u->name[NAME_MAX] = 0;
- u->mode = S_IFDIR|DEFAULT_DIRMODE;
- u->data = 0;
- u->fs = d->file_system;
- /* now get the file system its own device number */
- u->dev = d->dev_no = curr_dev_no++;
- /* chain new entry into unifile list */
- u->next = u_root;
- u_root = u;
- return (long)u->dev;
- } else if (cmd == FS_UNMOUNT) { /* remove a file system's directory */
- struct fs_descr *d = (struct fs_descr*)arg;
- FILESYS *fs;
- UNIFILE *u;
-
- /* first check that directory exists */
- /* use special uni_lookup mode to get the unifile entry */
- r = do_ulookup(dir, name, &fc, &u);
- if (r != 0) return EFILNF; /* name does not exist */
- if (!d) return EFILNF;
- if (!d->file_system) return EFILNF;
- if (d->file_system != fc.fs)
- return EFILNF; /* not the right name! */
- release_cookie(&fc);
-
- if (!u || (u->fs != d->file_system))
- return EFILNF;
- /* check if FS is installed */
- for (fs = active_fs; fs; fs = fs->next)
- if (d->file_system == fs) break;
- if (!fs) return EACCDN; /* not installed, so return an error */
-
- /* here comes the difficult part: we have to close all files on that
- * device, so we have to call changedrv(). The file system driver
- * has to make sure that further calls to fs.root() with this device
- * number will fail!
- *
- * Kludge: mark the directory as a link, so uni_remove will remove it.
- */
- changedrv(u->dev);
- u->mode &= ~S_IFMT;
- u->mode |= S_IFLNK;
- return uni_remove(dir, name);
- } else if (cmd == FS_UNINSTALL) { /* remove file system from kernel list */
- struct fs_descr *d = (struct fs_descr*)arg;
- FILESYS *fs, *last_fs;
- UNIFILE *u;
-
- /* first check if there are any files or directories associated with
- * this file system
- */
- for (u = u_root; u; u = u->next)
- if (u->fs == d->file_system)
- return EACCDN; /* we cannot remove it before unmount */
- last_fs = 0;
- fs = active_fs;
- while (fs) { /* go through the list and remove the file system */
- if (fs == d->file_system) {
- if (last_fs)
- last_fs->next = fs->next;
- else
- active_fs = fs->next;
- d->file_system->next = 0;
- return 0;
- }
- last_fs = fs;
- fs = fs->next;
- }
- return EFILNF;
- } else {
- /* see if we should just pass this along to another file system */
- r = uni_lookup(dir, name, &fc);
- if (r == 0) {
- if (fc.fs != &uni_filesys) {
- r = (*fc.fs->fscntl)(&fc, ".", cmd, arg);
- release_cookie(&fc);
- return r;
- }
- release_cookie(&fc);
- }
- }
- return EINVFN;
- }
-